1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26 package java.awt;
27
28 import java.awt.MultipleGradientPaint.CycleMethod;
29 import java.awt.MultipleGradientPaint.ColorSpaceType;
30 import java.awt.color.ColorSpace;
31 import java.awt.geom.AffineTransform;
32 import java.awt.geom.NoninvertibleTransformException;
33 import java.awt.geom.Rectangle2D;
34 import java.awt.image.ColorModel;
35 import java.awt.image.DataBuffer;
36 import java.awt.image.DataBufferInt;
37 import java.awt.image.DirectColorModel;
38 import java.awt.image.Raster;
39 import java.awt.image.SinglePixelPackedSampleModel;
40 import java.awt.image.WritableRaster;
41 import java.lang.ref.SoftReference;
42 import java.lang.ref.WeakReference;
43 import java.util.Arrays;
44
45
46
47
48
49
50
51
52
53 abstract class MultipleGradientPaintContext implements PaintContext {
54
55
56
57
58
59 protected ColorModel model;
60
61
62 private static ColorModel xrgbmodel =
63 new DirectColorModel(24, 0x00ff0000, 0x0000ff00, 0x000000ff);
64
65
66 protected static ColorModel cachedModel;
67
68
69 protected static WeakReference<Raster> cached;
70
71
72 protected Raster saved;
73
74
75 protected CycleMethod cycleMethod;
76
77
78 protected ColorSpaceType colorSpace;
79
80
81 protected float a00, a01, a10, a11, a02, a12;
82
83
84
85
86
87
88
89
90 protected boolean isSimpleLookup;
91
92
93
94
95
96 protected int fastGradientArraySize;
97
98
99
100
101
102
103 protected int[] gradient;
104
105
106
107
108
109 private int[][] gradients;
110
111
112 private float[] normalizedIntervals;
113
114
115 private float[] fractions;
116
117
118 private int transparencyTest;
119
120
121 private static final int SRGBtoLinearRGB[] = new int[256];
122 private static final int LinearRGBtoSRGB[] = new int[256];
123
124 static {
125
126 for (int k = 0; k < 256; k++) {
127 SRGBtoLinearRGB[k] = convertSRGBtoLinearRGB(k);
128 LinearRGBtoSRGB[k] = convertLinearRGBtoSRGB(k);
129 }
130 }
131
132
133
134
135
136 protected static final int GRADIENT_SIZE = 256;
137 protected static final int GRADIENT_SIZE_INDEX = GRADIENT_SIZE -1;
138
139
140
141
142
143
144
145 private static final int MAX_GRADIENT_ARRAY_SIZE = 5000;
146
147
148
149
150 protected MultipleGradientPaintContext(MultipleGradientPaint mgp,
151 ColorModel cm,
152 Rectangle deviceBounds,
153 Rectangle2D userBounds,
154 AffineTransform t,
155 RenderingHints hints,
156 float[] fractions,
157 Color[] colors,
158 CycleMethod cycleMethod,
159 ColorSpaceType colorSpace)
160 {
161 if (deviceBounds == null) {
162 throw new NullPointerException("Device bounds cannot be null");
163 }
164
165 if (userBounds == null) {
166 throw new NullPointerException("User bounds cannot be null");
167 }
168
169 if (t == null) {
170 throw new NullPointerException("Transform cannot be null");
171 }
172
173 if (hints == null) {
174 throw new NullPointerException("RenderingHints cannot be null");
175 }
176
177
178
179 AffineTransform tInv;
180 try {
181
182
183 t.invert();
184 tInv = t;
185 } catch (NoninvertibleTransformException e) {
186
187
188 tInv = new AffineTransform();
189 }
190 double m[] = new double[6];
191 tInv.getMatrix(m);
192 a00 = (float)m[0];
193 a10 = (float)m[1];
194 a01 = (float)m[2];
195 a11 = (float)m[3];
196 a02 = (float)m[4];
197 a12 = (float)m[5];
198
199
200 this.cycleMethod = cycleMethod;
201 this.colorSpace = colorSpace;
202
203
204 this.fractions = fractions;
205
206
207
208
209 int[] gradient =
210 (mgp.gradient != null) ? mgp.gradient.get() : null;
211 int[][] gradients =
212 (mgp.gradients != null) ? mgp.gradients.get() : null;
213
214 if (gradient == null && gradients == null) {
215
216 calculateLookupData(colors);
217
218
219
220 mgp.model = this.model;
221 mgp.normalizedIntervals = this.normalizedIntervals;
222 mgp.isSimpleLookup = this.isSimpleLookup;
223 if (isSimpleLookup) {
224
225 mgp.fastGradientArraySize = this.fastGradientArraySize;
226 mgp.gradient = new SoftReference<int[]>(this.gradient);
227 } else {
228
229 mgp.gradients = new SoftReference<int[][]>(this.gradients);
230 }
231 } else {
232
233 this.model = mgp.model;
234 this.normalizedIntervals = mgp.normalizedIntervals;
235 this.isSimpleLookup = mgp.isSimpleLookup;
236 this.gradient = gradient;
237 this.fastGradientArraySize = mgp.fastGradientArraySize;
238 this.gradients = gradients;
239 }
240 }
241
242
243
244
245
246
247 private void calculateLookupData(Color[] colors) {
248 Color[] normalizedColors;
249 if (colorSpace == ColorSpaceType.LINEAR_RGB) {
250
251 normalizedColors = new Color[colors.length];
252
253 for (int i = 0; i < colors.length; i++) {
254 int argb = colors[i].getRGB();
255 int a = argb >>> 24;
256 int r = SRGBtoLinearRGB[(argb >> 16) & 0xff];
257 int g = SRGBtoLinearRGB[(argb >> 8) & 0xff];
258 int b = SRGBtoLinearRGB[(argb ) & 0xff];
259 normalizedColors[i] = new Color(r, g, b, a);
260 }
261 } else {
262
263
264 normalizedColors = colors;
265 }
266
267
268 normalizedIntervals = new float[fractions.length-1];
269
270
271 for (int i = 0; i < normalizedIntervals.length; i++) {
272
273 normalizedIntervals[i] = this.fractions[i+1] - this.fractions[i];
274 }
275
276
277 transparencyTest = 0xff000000;
278
279
280 gradients = new int[normalizedIntervals.length][];
281
282
283 float Imin = 1;
284 for (int i = 0; i < normalizedIntervals.length; i++) {
285 Imin = (Imin > normalizedIntervals[i]) ?
286 normalizedIntervals[i] : Imin;
287 }
288
289
290
291
292
293
294 int estimatedSize = 0;
295 for (int i = 0; i < normalizedIntervals.length; i++) {
296 estimatedSize += (normalizedIntervals[i]/Imin) * GRADIENT_SIZE;
297 }
298
299 if (estimatedSize > MAX_GRADIENT_ARRAY_SIZE) {
300
301 calculateMultipleArrayGradient(normalizedColors);
302 } else {
303
304 calculateSingleArrayGradient(normalizedColors, Imin);
305 }
306
307
308 if ((transparencyTest >>> 24) == 0xff) {
309 model = xrgbmodel;
310 } else {
311 model = ColorModel.getRGBdefault();
312 }
313 }
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338 private void calculateSingleArrayGradient(Color[] colors, float Imin) {
339
340 isSimpleLookup = true;
341
342
343 int rgb1, rgb2;
344
345
346 int gradientsTot = 1;
347
348
349 for (int i = 0; i < gradients.length; i++) {
350
351
352 int nGradients = (int)((normalizedIntervals[i]/Imin)*255f);
353 gradientsTot += nGradients;
354 gradients[i] = new int[nGradients];
355
356
357 rgb1 = colors[i].getRGB();
358 rgb2 = colors[i+1].getRGB();
359
360
361 interpolate(rgb1, rgb2, gradients[i]);
362
363
364
365 transparencyTest &= rgb1;
366 transparencyTest &= rgb2;
367 }
368
369
370 gradient = new int[gradientsTot];
371 int curOffset = 0;
372 for (int i = 0; i < gradients.length; i++){
373 System.arraycopy(gradients[i], 0, gradient,
374 curOffset, gradients[i].length);
375 curOffset += gradients[i].length;
376 }
377 gradient[gradient.length-1] = colors[colors.length-1].getRGB();
378
379
380
381 if (colorSpace == ColorSpaceType.LINEAR_RGB) {
382 for (int i = 0; i < gradient.length; i++) {
383 gradient[i] = convertEntireColorLinearRGBtoSRGB(gradient[i]);
384 }
385 }
386
387 fastGradientArraySize = gradient.length - 1;
388 }
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408 private void calculateMultipleArrayGradient(Color[] colors) {
409
410 isSimpleLookup = false;
411
412
413 int rgb1, rgb2;
414
415
416 for (int i = 0; i < gradients.length; i++){
417
418
419 gradients[i] = new int[GRADIENT_SIZE];
420
421
422 rgb1 = colors[i].getRGB();
423 rgb2 = colors[i+1].getRGB();
424
425
426 interpolate(rgb1, rgb2, gradients[i]);
427
428
429
430 transparencyTest &= rgb1;
431 transparencyTest &= rgb2;
432 }
433
434
435
436 if (colorSpace == ColorSpaceType.LINEAR_RGB) {
437 for (int j = 0; j < gradients.length; j++) {
438 for (int i = 0; i < gradients[j].length; i++) {
439 gradients[j][i] =
440 convertEntireColorLinearRGBtoSRGB(gradients[j][i]);
441 }
442 }
443 }
444 }
445
446
447
448
449
450
451
452
453
454 private void interpolate(int rgb1, int rgb2, int[] output) {
455
456 int a1, r1, g1, b1, da, dr, dg, db;
457
458
459 float stepSize = 1.0f / output.length;
460
461
462 a1 = (rgb1 >> 24) & 0xff;
463 r1 = (rgb1 >> 16) & 0xff;
464 g1 = (rgb1 >> 8) & 0xff;
465 b1 = (rgb1 ) & 0xff;
466
467
468 da = ((rgb2 >> 24) & 0xff) - a1;
469 dr = ((rgb2 >> 16) & 0xff) - r1;
470 dg = ((rgb2 >> 8) & 0xff) - g1;
471 db = ((rgb2 ) & 0xff) - b1;
472
473
474
475
476 for (int i = 0; i < output.length; i++) {
477 output[i] =
478 (((int) ((a1 + i * da * stepSize) + 0.5) << 24)) |
479 (((int) ((r1 + i * dr * stepSize) + 0.5) << 16)) |
480 (((int) ((g1 + i * dg * stepSize) + 0.5) << 8)) |
481 (((int) ((b1 + i * db * stepSize) + 0.5) ));
482 }
483 }
484
485
486
487
488
489
490 private int convertEntireColorLinearRGBtoSRGB(int rgb) {
491
492 int a1, r1, g1, b1;
493
494
495 a1 = (rgb >> 24) & 0xff;
496 r1 = (rgb >> 16) & 0xff;
497 g1 = (rgb >> 8) & 0xff;
498 b1 = (rgb ) & 0xff;
499
500
501 r1 = LinearRGBtoSRGB[r1];
502 g1 = LinearRGBtoSRGB[g1];
503 b1 = LinearRGBtoSRGB[b1];
504
505
506 return ((a1 << 24) |
507 (r1 << 16) |
508 (g1 << 8) |
509 (b1 ));
510 }
511
512
513
514
515
516
517
518
519
520
521
522 protected final int indexIntoGradientsArrays(float position) {
523
524 if (cycleMethod == CycleMethod.NO_CYCLE) {
525 if (position > 1) {
526
527 position = 1;
528 } else if (position < 0) {
529
530 position = 0;
531 }
532 } else if (cycleMethod == CycleMethod.REPEAT) {
533
534
535 position = position - (int)position;
536
537
538 if (position < 0) {
539
540 position = position + 1;
541 }
542 } else {
543 if (position < 0) {
544
545 position = -position;
546 }
547
548
549 int part = (int)position;
550
551
552 position = position - part;
553
554 if ((part & 1) == 1) {
555
556 position = 1 - position;
557 }
558 }
559
560
561
562 if (isSimpleLookup) {
563
564 return gradient[(int)(position * fastGradientArraySize)];
565 } else {
566
567
568
569 for (int i = 0; i < gradients.length; i++) {
570 if (position < fractions[i+1]) {
571
572 float delta = position - fractions[i];
573
574
575 int index = (int)((delta / normalizedIntervals[i])
576 * (GRADIENT_SIZE_INDEX));
577
578 return gradients[i][index];
579 }
580 }
581 }
582
583 return gradients[gradients.length - 1][GRADIENT_SIZE_INDEX];
584 }
585
586
587
588
589
590 private static int convertSRGBtoLinearRGB(int color) {
591 float input, output;
592
593 input = color / 255.0f;
594 if (input <= 0.04045f) {
595 output = input / 12.92f;
596 } else {
597 output = (float)Math.pow((input + 0.055) / 1.055, 2.4);
598 }
599
600 return Math.round(output * 255.0f);
601 }
602
603
604
605
606
607 private static int convertLinearRGBtoSRGB(int color) {
608 float input, output;
609
610 input = color/255.0f;
611 if (input <= 0.0031308) {
612 output = input * 12.92f;
613 } else {
614 output = (1.055f *
615 ((float) Math.pow(input, (1.0 / 2.4)))) - 0.055f;
616 }
617
618 return Math.round(output * 255.0f);
619 }
620
621
622
623
624 public final Raster getRaster(int x, int y, int w, int h) {
625
626
627 Raster raster = saved;
628 if (raster == null ||
629 raster.getWidth() < w || raster.getHeight() < h)
630 {
631 raster = getCachedRaster(model, w, h);
632 saved = raster;
633 }
634
635
636
637
638
639
640
641
642 DataBufferInt rasterDB = (DataBufferInt)raster.getDataBuffer();
643 int[] pixels = rasterDB.getData(0);
644 int off = rasterDB.getOffset();
645 int scanlineStride = ((SinglePixelPackedSampleModel)
646 raster.getSampleModel()).getScanlineStride();
647 int adjust = scanlineStride - w;
648
649 fillRaster(pixels, off, adjust, x, y, w, h);
650
651 return raster;
652 }
653
654 protected abstract void fillRaster(int pixels[], int off, int adjust,
655 int x, int y, int w, int h);
656
657
658
659
660
661
662
663 private static synchronized Raster getCachedRaster(ColorModel cm,
664 int w, int h)
665 {
666 if (cm == cachedModel) {
667 if (cached != null) {
668 Raster ras = (Raster) cached.get();
669 if (ras != null &&
670 ras.getWidth() >= w &&
671 ras.getHeight() >= h)
672 {
673 cached = null;
674 return ras;
675 }
676 }
677 }
678 return cm.createCompatibleWritableRaster(w, h);
679 }
680
681
682
683
684
685
686 private static synchronized void putCachedRaster(ColorModel cm,
687 Raster ras)
688 {
689 if (cached != null) {
690 Raster cras = (Raster) cached.get();
691 if (cras != null) {
692 int cw = cras.getWidth();
693 int ch = cras.getHeight();
694 int iw = ras.getWidth();
695 int ih = ras.getHeight();
696 if (cw >= iw && ch >= ih) {
697 return;
698 }
699 if (cw * ch >= iw * ih) {
700 return;
701 }
702 }
703 }
704 cachedModel = cm;
705 cached = new WeakReference<Raster>(ras);
706 }
707
708
709
710
711 public final void dispose() {
712 if (saved != null) {
713 putCachedRaster(model, saved);
714 saved = null;
715 }
716 }
717
718
719
720
721 public final ColorModel getColorModel() {
722 return model;
723 }
724 }